using System;
using System.Collections.Generic;
using System.IO;
using Sce.PlayStation.Core;
namespace Nyanimals
{
    public class CAnimation
    {
        private string name;
        private float duration;
        private int rootBoneIndex;
        private List<tBone> bones;
        private Dictionary<string, int> attributes;
        private List<Matrix4> bindPoseInverse;
        
        private void LoadAnimation(string szName)
        {
            BinaryReader read = new BinaryReader(File.Open(szName, FileMode.Open, FileAccess.Read));
            
            // check file header
            string header = new string(read.ReadChars(4));
        
            if(header != "ANIM")
            {
                // TODO: handle wrong file type
                return;
            }
        
            // basic data shit
            duration = read.ReadSingle();
            int numBones = read.ReadInt32();
            
            bones = new List<tBone>(numBones);
        
            // read in the attributes
            int nNumAttributes = read.ReadInt32();
            attributes = new Dictionary<string, int>();
            for(int attributeIndex = 0; attributeIndex < nNumAttributes; ++attributeIndex)
            {
                int nNameSize = read.ReadInt32();
                char[] attributeName = new char[nNameSize];
                attributeName = read.ReadChars(nNameSize);
        
                int nValue = read.ReadInt32();
        
                // TODO: handle attributes that already exist(like in settings.cpp/settings.xml)
                attributes[new string(attributeName)] = nValue;
            }
            // read each bone
            rootBoneIndex = -1;
            for(int boneCount = 0; boneCount < numBones; ++boneCount)
            {
                tBone bone = new tBone();
                   bone.ChildrenIndices = new List<int>();
                
                // read in the bone index
                bone.MyIndex = read.ReadInt32();
        
                // read in bone name
                int nNameSize = read.ReadInt32();
                bone.Name = new string(read.ReadChars(nNameSize));
        
                // read in the parent
                bone.ParentIndex = read.ReadInt32();
                
               // get our root bone
               if(bone.ParentIndex == -1)
               {
                   if(rootBoneIndex > -1)
                   {
                       throw new NullReferenceException("Multiple Root Bones");
                   }
                   rootBoneIndex = bone.MyIndex;
               }
               
               // set us as a child of our parent
               if(bone.ParentIndex > -1)
               {
                   tBone parentBone = bones[bone.ParentIndex];
                   parentBone.ChildrenIndices.Add(bone.MyIndex);
               }
                   
                // read in each keyframe
                int numKeyFrames = read.ReadInt32();
                bone.KeyFrames = new List<tKeyFrame>(numKeyFrames);
                for(int keyCount = 0; keyCount < numKeyFrames; ++keyCount)
                {
                    int keyNumber = read.ReadInt32();
        
                    tKeyFrame newKey = new tKeyFrame();
                    newKey.KeyTime = read.ReadSingle();
                    newKey.LocalMatrix = new Matrix4(read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), 
                                                     read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), 
                                                     read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), 
                                                     read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), read.ReadSingle());
                    
                    // TODO: remove the world from the exporter
                    /*newKey.WorldMatrix = */Matrix4 GARBAGE = new Matrix4(read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), 
                                                     read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), 
                                                     read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), 
                                                     read.ReadSingle(), read.ReadSingle(), read.ReadSingle(), read.ReadSingle());
                    
                    bone.KeyFrames.Add(newKey);
                }
                   
                   // add it
                bones.Add(bone);                
                   
            }
            
            
            // build the bind pose inverses
            Matrix4[] bindInv = new Matrix4[numBones];
            TraverseInvBind(rootBoneIndex, bindInv);
            bindPoseInverse = new List<Matrix4>(bindInv);
            /*
            for(int bindIndex = 0; bindIndex < numBones; ++bindIndex)
            {
                Matrix4 bindInv = bones[bindIndex].KeyFrames[0].WorldMatrix.Inverse();
                bindPoseInverse.Add(bindInv);
            }  
            */
            
        }
    private void TraverseInvBind(int curIndex, Matrix4[] bindInv)
    {
            tBone curBone = bones[curIndex];
            Matrix4 curMat = curBone.KeyFrames[0].LocalMatrix;
            
            // apply parent if applicable
            if(curBone.ParentIndex > -1)
            {
                Matrix4 parentMat = bindInv[curBone.ParentIndex];
                curMat = parentMat * curMat;
            }
            
            // save what we are for the kids
            bindInv[curIndex] = curMat;
            
            // go through the kiddies
            foreach(int childIndex in curBone.ChildrenIndices)
            {
                TraverseInvBind(childIndex, bindInv);
            }
            
            // we can now inverse the bitch
            bindInv[curIndex] = curMat.Inverse();
    }
     
     public List<Matrix4> BindPoseInverse
     {
         get { return bindPoseInverse;}
     }
     
     public string Name
     {
         get { return name;}
         set { name = value;}
     }
     public float Duration
     {
         get { return duration;}
     }
        public int RootBoneIndex
        {
            get { return rootBoneIndex;}
        }
        public tBone RootBone
        {
            get { return bones[rootBoneIndex];}
        }
     public List<tBone> Bones
     {
         get { return bones;}
     }
     public int Attribute(string szAttribute)
     {
         return attributes[szAttribute];
     }
     
     public CAnimation(string szName)
     {
         duration = 0.0f;            
         LoadAnimation(szName);
     }
     
 }
}

  
         